/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.persistence.hibernate.manager; // NOPMD

import java.io.Serializable;
import java.util.List;

import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.HibernateTemplate;

import com.genia.toolbox.basics.bean.IterableIterator;
import com.genia.toolbox.basics.manager.CollectionManager;
import com.genia.toolbox.basics.tools.shared.PropertiesClass;
import com.genia.toolbox.persistence.basis.manager.AbstractPersistenceManager;
import com.genia.toolbox.persistence.bean.Persistable;
import com.genia.toolbox.persistence.bean.QueryParameters;
import com.genia.toolbox.persistence.criteria.Criteria;
import com.genia.toolbox.persistence.criteria.CriteriaResult;
import com.genia.toolbox.persistence.exception.PersistenceException;
import com.genia.toolbox.persistence.hibernate.criteria.HibernateCriteria;
import com.genia.toolbox.persistence.manager.FieldManager;
import com.genia.toolbox.persistence.manager.OrderManager;
import com.genia.toolbox.persistence.manager.RestrictionsManager;

/**
 * Hibernate implementation of the persistent manager for saving, updating or
 * deleting an instance of persistable object.
 */
public class HibernatePersistenceManager extends AbstractPersistenceManager {

	/**
	 * flush eventual cached elements.
	 * 
	 * @see com.genia.toolbox.persistence.basis.manager.AbstractPersistenceManager#clear()
	 */
	@Override
	public void clear() {
		getHibernateTemplate().flush();
		getHibernateTemplate().clear();
	}

	/**
	 * This method iterates over the results of a criteria.
	 * 
	 * @param <MAINTYPE>
	 *            the main type of the criteria
	 * @param criteria
	 *            the criteria that describes the query
	 * @param parameters
	 *            the parameters of this search
	 * @return the list of <code>CriteriaResult</code> that represents the
	 *         result of the query
	 * @throws PersistenceException
	 *             when the is a problem with the persisence layer
	 * @see com.genia.toolbox.persistence.basis.manager.AbstractPersistenceManager#iterate(com.genia.toolbox.persistence.criteria.Criteria,
	 *      com.genia.toolbox.persistence.bean.QueryParameters)
	 */
	@Override
	public <MAINTYPE> IterableIterator<CriteriaResult<MAINTYPE>> iterate(
			Criteria<MAINTYPE> criteria, QueryParameters parameters)
			throws PersistenceException {
		return getMyHibernatePersistenceManager().iterate(criteria, parameters);
	}

	/**
	 * which character to use to escape like expression.
	 */
	public static final char ESCAPE_CHARACTER = '@';

	/**
	 * the <code>CollectionManager</code> that is used to perform joins on
	 * <code>Collection</code>.
	 */
	private CollectionManager collectionManager;

	/**
	 * the <code>FieldManager</code> to use with this implementation.
	 */
	private HibernateFieldManager fieldManager;

	/**
	 * the <code>HibernateTemplate</code> that wrapps all hibernate operation.
	 */
	private transient HibernateTemplate hibernateTemplate;

	/**
	 * the <code>OrderManager</code> to use with this implementation.
	 */
	private HibernateOrderManager orderManager;

	/**
	 * the <code>RestrictionsManager</code> to use with this implementation.
	 */
	private HibernateRestrictionsManager restrictionsManager;

	/**
	 * the manager for the transactional actions.
	 */
	private IHibernatePersistenceManager myHibernatePersistenceManager;
	
	private TransactionalHibernatePersistenceManager transactionalHibernatePersistenceManager;
	
	private NonTransactionalHibernatePersistenceManager nonTransactionalHibernatePersistenceManager;

	/**
	 * This method deletes an object.
	 * 
	 * @param object
	 *            The object to delete
	 * @throws PersistenceException
	 *             when an error occurred.
	 */
	public void delete(final Persistable<? extends Serializable> object)
			throws PersistenceException {
		getMyHibernatePersistenceManager().delete(object);
	}

	/**
	 * This method returns the results of a criteria.
	 * 
	 * @param <MAINTYPE>
	 *            the main type of the criteria.
	 * @param criteria
	 *            the criteria that describes the query
	 * @param parameters
	 *            the parameters of this search
	 * @return the list of <code>CriteriaResult</code> that represents the
	 *         result of the query
	 * @throws PersistenceException
	 *             when the is a problem with the persisence layer
	 */
	public <MAINTYPE> List<CriteriaResult<MAINTYPE>> find(
			final Criteria<MAINTYPE> criteria, final QueryParameters parameters)
			throws PersistenceException {
		return getMyHibernatePersistenceManager().find(criteria, parameters);
	}

	/**
	 * This method get an persited object given its class and identifier.
	 * 
	 * @param <IDENTIFIER>
	 *            The type of the identifier
	 * @param <TYPE>
	 *            The type of the object to retrieve
	 * @param type
	 *            The class of the object to retrieve
	 * @param identifier
	 *            The identifier of the object to retrieve
	 * @return the object of class <code>type</code> with the identifier
	 *         <code>identifier</code> or <code>null</code> if no such
	 *         object exists
	 */
	public <IDENTIFIER extends Serializable, TYPE extends Persistable<IDENTIFIER>> TYPE get(
			final Class<TYPE> type, final IDENTIFIER identifier) {
		return getMyHibernatePersistenceManager().get(type, identifier);
	}

	/**
	 * This methods return all the persisted objects of a given type.
	 * 
	 * @param <TYPE>
	 *            the type of the objects to retrieve
	 * @param type
	 *            the class of the objects to retrieve
	 * @return the list of all objects of the given tyoe
	 * @throws PersistenceException
	 *             when the is a problem with the persisence layer
	 */
	public <TYPE> List<TYPE> getAll(final Class<TYPE> type)
			throws PersistenceException {
		return simpleFind(getCriteria(type));
	}

	/**
	 * getter for the collectionManager property.
	 * 
	 * @return the collectionManager
	 */
	public CollectionManager getCollectionManager() {
		return collectionManager;
	}

	/**
	 * This methods return a new <code>Criteria</code> for the object of type
	 * <code>mainType</code>.
	 * 
	 * @param <MAINTYPE>
	 *            The main type of the criteria
	 * @param mainType
	 *            The class of the main type of the criteria
	 * @return a <code>Criteria</code> to describe a new query
	 */
	public <MAINTYPE> Criteria<MAINTYPE> getCriteria(
			final Class<MAINTYPE> mainType) {
		return new HibernateCriteria<MAINTYPE>(this, mainType);
	}

	/**
	 * getter for the fieldManager property.
	 * 
	 * @return the fieldManager
	 */
	public FieldManager getFieldManager() {
		return fieldManager;
	}

	/**
	 * getter for the orderManager property.
	 * 
	 * @return the orderManager
	 */
	public OrderManager getOrderManager() {
		return orderManager;
	}

	/**
	 * getter for the restrictionsManager property.
	 * 
	 * @return the restrictionsManager
	 */
	public RestrictionsManager getRestrictionsManager() {
		return restrictionsManager;
	}



	/**
	 * This method saves a new object.
	 * 
	 * @param object
	 *            The object to save
	 * @throws PersistenceException
	 *             when an error occurred
	 */
	public void save(final Persistable<? extends Serializable> object)
			throws PersistenceException {
		getMyHibernatePersistenceManager().save(object);
	}

	/**
	 * This method saves a new object or update an already persistent object.
	 * This method should be avoided as much as possible. Finding if an object
	 * is persistent or not is tedious and error prone, unexpected comportment
	 * can arrise.
	 * 
	 * @param object
	 *            The object to save or update
	 * @throws PersistenceException
	 *             if any problems occur during persistence process
	 * @see com.genia.toolbox.persistence.manager.PersistenceManager#saveOrUpdate(com.genia.toolbox.persistence.bean.Persistable)
	 */
	public void saveOrUpdate(Persistable<? extends Serializable> object)
			throws PersistenceException {
		getMyHibernatePersistenceManager().saveOrUpdate(object);
	}

	/**
	 * setter for the collectionManager property.
	 * 
	 * @param collectionManager
	 *            the collectionManager to set
	 */
	public void setCollectionManager(final CollectionManager collectionManager) {
		this.collectionManager = collectionManager;
	}

	/**
	 * setter for the fieldManager property.
	 * 
	 * @param fieldManager
	 *            the fieldManager to set
	 */
	public void setFieldManager(final FieldManager fieldManager) {
		this.fieldManager = (HibernateFieldManager) fieldManager;
	}

	/**
	 * setter for the orderManager property.
	 * 
	 * @param orderManager
	 *            the orderManager to set
	 */
	public void setOrderManager(final OrderManager orderManager) {
		this.orderManager = (HibernateOrderManager) orderManager;
	}

	/**
	 * setter for the restrictionsManager property.
	 * 
	 * @param restrictionsManager
	 *            the restrictionsManager to set
	 */
	public void setRestrictionsManager(
			final RestrictionsManager restrictionsManager) {
		this.restrictionsManager = (HibernateRestrictionsManager) restrictionsManager;
	}

	/**
	 * the setter for the injected hibernate <code>SessionFactory</code>.
	 * 
	 * @param sessionFactory
	 *            the injection hibernate <code>SessionFactory</code>
	 */
	public void setSessionFactory(final SessionFactory sessionFactory) {
		this.hibernateTemplate = new HibernateTemplate(sessionFactory);
	}

	/**
	 * This method updates an object.
	 * 
	 * @param object
	 *            The object to update
	 * @throws PersistenceException
	 *             when an error occurred
	 */
	public void update(final Persistable<? extends Serializable> object)
			throws PersistenceException {
		getMyHibernatePersistenceManager().update(object);
	}

	/**
	 * getter for the hibernateTemplate property.
	 * 
	 * @return the hibernateTemplate
	 */
	HibernateTemplate getHibernateTemplate() {
		return hibernateTemplate;
	}

	public NonTransactionalHibernatePersistenceManager getNonTransactionalHibernatePersistenceManager() {
		return nonTransactionalHibernatePersistenceManager;
	}

	public void setNonTransactionalHibernatePersistenceManager(
			NonTransactionalHibernatePersistenceManager nonTransactionalHibernatePersistenceManager) {
		this.nonTransactionalHibernatePersistenceManager = nonTransactionalHibernatePersistenceManager;
	}

	public TransactionalHibernatePersistenceManager getTransactionalHibernatePersistenceManager() {
		return transactionalHibernatePersistenceManager;
	}

	public void setTransactionalHibernatePersistenceManager(
			TransactionalHibernatePersistenceManager transactionalHibernatePersistenceManager) {
		this.transactionalHibernatePersistenceManager = transactionalHibernatePersistenceManager;
	}
	public IHibernatePersistenceManager getMyHibernatePersistenceManager() {
		if(PropertiesClass.getSupressReadOnlyTransactions()) {
			return getNonTransactionalHibernatePersistenceManager();
			
		} else {
			return getTransactionalHibernatePersistenceManager();
			
		}
	}

	public void setMyHibernatePersistenceManager(
			IHibernatePersistenceManager myHibernatePersistenceManager) {
		this.myHibernatePersistenceManager = myHibernatePersistenceManager;
	}
}
